
#include <gtk-3.0/gtk/gtk.h>
#include <stdlib.h>
#include <string.h>
#include "tux_gsettings.h"
#include "gstreamer_util.h"
#include <gstreamer-1.0/gst/gst.h>
#include <gstreamer-1.0/gst/app/app.h>
#include "track_box_widget.h"
#include "main_window_widget.h"
#include <math.h>




struct _track_box_widget_class
{
    GtkBoxClass parent_class;
};

struct track_box_widget_private 
{
    GtkWidget *record_toggle_button, *track_label, *track_device_combobox, *track_volume_levelbar;
    
};

struct _track_box_widget 
{
    GtkBox parent;
    TrackBoxWidgetPrivate *priv;
    tux_gst_pipeline *audio_in_pipeline;
    TrackDataWidget *track_data_widget;
    
};

typedef struct _track_box_widget TrackBoxWidget;
typedef struct _track_box_widget_class TrackBoxWidgetClass;
typedef struct track_box_widget_private TrackBoxWidgetPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(TrackBoxWidget,track_box_widget,GTK_TYPE_BOX);

TrackDataWidget *get_track_data_widget_from_track_box_widget(TrackBoxWidget *widget) {
    return widget->track_data_widget;
}

void add_audio_buffer_to_track_data(TrackBoxWidget *widget, GstMapInfo map) {
    guint i;
    gint8 *data = map.data;
    if (is_record_active) {
        GArray *values = get_current_track_data_small_values(widget->track_data_widget);
        g_array_append_vals(values,data,map.size);
    }
}

enum {
    PROP_0,
    PROP_AUDIO_DEVICES_IN
};

void prepare_for_recording(TrackBoxWidget *widget) {
    new_current_track_data(widget->track_data_widget);
}

void stop_recording(TrackBoxWidget *widget) {
    close_current_track_data(widget->track_data_widget);
}

static gboolean
bus_call (GstBus     *bus,
          GstMessage *msg,
          gpointer data)
{
    const GstStructure *s = gst_message_get_structure(msg);
    const gchar *name = gst_structure_get_name(s);
    TrackBoxWidget *widget = TRACK_BOX_WIDGET(data);
    
  switch (GST_MESSAGE_TYPE (msg)) {
    
      case GST_MESSAGE_ELEMENT:
        
 
    if (strcmp(name, "level") == 0) {
      gint channels;
      GstClockTime endtime;
      gdouble rms_dB, peak_dB, decay_dB;
      gdouble rms;
      const GValue *array_val;
      const GValue *value;
      GValueArray *rms_arr, *peak_arr, *decay_arr;
      gint i;

      if (!gst_structure_get_clock_time (s, "endtime", &endtime))
        g_warning ("Could not parse endtime");

      /* the values are packed into GValueArrays with the value per channel */
      array_val = gst_structure_get_value (s, "rms");
      rms_arr = (GValueArray *) g_value_get_boxed (array_val);

      array_val = gst_structure_get_value (s, "peak");
      peak_arr = (GValueArray *) g_value_get_boxed (array_val);

      array_val = gst_structure_get_value (s, "decay");
      decay_arr = (GValueArray *) g_value_get_boxed (array_val);

      /* we can get the number of channels as the length of any of the value
       * arrays */
      channels = rms_arr->n_values;
      //g_debug ("endtime: %" GST_TIME_FORMAT ", channels: %d\n",
       //   GST_TIME_ARGS (endtime), channels);
      if (channels != 1) {
          g_critical("input channels differ from 1: %d",channels);
      }
      //value = g_value_array_get_nth (rms_arr, i);
        value = rms_arr->values; 
        rms_dB = g_value_get_double (value);

        //value = g_value_array_get_nth (peak_arr, i);
        value = peak_arr->values;
        peak_dB = g_value_get_double (value);

        //value = g_value_array_get_nth (decay_arr, i);
        value = decay_arr->values;
        decay_dB = g_value_get_double (value);
        //g_debug ("    RMS: %f dB, peak: %f dB, decay: %f dB\n",
         //   rms_dB, peak_dB, decay_dB);

        /* converting from dB to normal gives us a value between 0.0 and 1.0 */
        rms = pow (10, rms_dB / 20);
        //g_debug ("    normalized rms value: %f\n", rms);
        gtk_level_bar_set_value(GTK_LEVEL_BAR(widget->priv->track_volume_levelbar),rms);
      
    }    
        break;
    case GST_MESSAGE_EOS:
      g_print ("End of stream\n");
      break;

    case GST_MESSAGE_ERROR: {
      gchar  *debug;
      GError *error;

      gst_message_parse_error (msg, &error, &debug);
      g_print("debug: %s\n",debug);
      g_free (debug);

      g_printerr ("Error: %s\n", error->message);
      g_error_free (error);

      break;
    }
    default:
      break;
  }

  return TRUE;
}

static GstFlowReturn new_sample(GstElement *sink, gpointer data) {
    GstSample *sample = gst_app_sink_pull_sample(GST_APP_SINK(sink));
    TrackBoxWidget *widget = TRACK_BOX_WIDGET(data);
    int i=0;
    if (sample != NULL) {
        GstBuffer *buffer = gst_sample_get_buffer(sample); 
        GstMapInfo map;
        if (buffer != NULL) {
            gst_buffer_map(buffer, &map, GST_MAP_READ);
            add_audio_buffer_to_track_data(widget,map);
            gst_buffer_unmap(buffer, &map);
        
         }
        gst_sample_unref(sample);
    }
    return GST_FLOW_OK;
}

static void track_box_widget_finalize(GObject *object) {
    TrackBoxWidget *widget = TRACK_BOX_WIDGET (object);
    tux_gst_pipeline_free(widget->audio_in_pipeline);
}

static void track_box_widget_init(TrackBoxWidget *widget) {
    widget->priv = G_TYPE_INSTANCE_GET_PRIVATE(widget, TRACK_BOX_WIDGET_TYPE, TrackBoxWidgetPrivate);
    gtk_widget_init_template(GTK_WIDGET(widget));
}


static void track_box_widget_set_audio_in_devices(TrackBoxWidget *widget, GVariant *audio_in_devices) {
    GVariantIter *iter = g_variant_iter_new(audio_in_devices);
    const gchar *audio_in_server_name, *audio_in_device_name;
    GtkTreeModel *list_store = gtk_combo_box_get_model(GTK_COMBO_BOX(widget->priv->track_device_combobox));
    char *device_str=malloc(40);
    while (g_variant_iter_next(iter,"(&s&s)",&audio_in_server_name,&audio_in_device_name)) {
        sprintf(device_str,"%s: %s",audio_in_server_name,audio_in_device_name);
        gtk_list_store_insert_with_values(GTK_LIST_STORE(list_store), NULL, -1, 0, device_str,1, device_str,-1);
    }
    free(device_str);
    g_variant_iter_free(iter);
       
}


static GVariant *track_box_widget_get_audio_in_devices(TrackBoxWidget *widget) {
    return NULL;
}

static void
track_box_widget_set_property (GObject         *object,
                         guint            prop_id,
                         const GValue    *value,
                         GParamSpec      *pspec)
{
  TrackBoxWidget *widget = TRACK_BOX_WIDGET (object);

  switch (prop_id)
    {
    case PROP_AUDIO_DEVICES_IN:
        track_box_widget_set_audio_in_devices(widget, g_value_get_variant(value));
          break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
track_box_widget_get_property (GObject         *object,
                        guint            prop_id,
                        GValue          *value,
                        GParamSpec      *pspec)
{
  TrackBoxWidget *widget = TRACK_BOX_WIDGET (object);

  switch (prop_id)
    {
    case PROP_AUDIO_DEVICES_IN:
      g_value_set_variant (value, track_box_widget_get_audio_in_devices(widget));      
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}


static gboolean draw_callback (GtkWidget *widget, cairo_t *cr, gpointer data)
{
  guint width, height;
  GdkRGBA color;

  width = gtk_widget_get_allocated_width (widget);
  height = gtk_widget_get_allocated_height (widget);
  g_print("cairo width %d height %d",width,height);
  cairo_set_source_rgb(cr, 0, 0, 0);
  cairo_set_line_width(cr, 0.5);
  cairo_move_to(cr, 0, height/2);
  cairo_line_to(cr, 500, height/2);
  cairo_stroke(cr);
 return FALSE;
}

static void track_device_combobox_changed_cb(TrackBoxWidget *widget, GtkWidget *combo_widget) {
    const gchar *active=gtk_combo_box_get_active_id(GTK_COMBO_BOX(combo_widget));
    gchar *active_text = strdup(active);
    gchar *active_text_tofree = active_text;
    char *server_name = strsep(&active_text, ": ");
    active_text++;
    g_debug("chosen server: %s device: %s",server_name,active_text);
    if (strcmp(server_name, "none") == 0) {
        if (tux_gst_pipeline_has_source(widget->audio_in_pipeline)) {
            tux_gst_pipeline_remove_source(widget->audio_in_pipeline);
        }
    } else {
        if (tux_gst_pipeline_has_source(widget->audio_in_pipeline)) {
            tux_gst_pipeline_replace_source(server_name,active_text,widget->audio_in_pipeline);
        } else {
            tux_gst_pipeline_add_source(server_name,active_text,widget->audio_in_pipeline);
        }
    }
    free(active_text_tofree);
}

static void record_toggle_button_toggled_cb(TrackBoxWidget *widget, GtkToggleButton *button) {
    g_debug("is active: %d\n",gtk_toggle_button_get_active(button));
    if (gtk_toggle_button_get_active(button)) {
        tux_gst_pipeline_set_state_playing(widget->audio_in_pipeline);
    } else {
        gtk_level_bar_set_value(GTK_LEVEL_BAR(widget->priv->track_volume_levelbar),0);
        tux_gst_pipeline_set_state_paused(widget->audio_in_pipeline);
    }
}



static void track_box_widget_class_init(TrackBoxWidgetClass *class) {
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class);
    GObjectClass *gobject_class = G_OBJECT_CLASS(class);
    
    gobject_class->set_property=track_box_widget_set_property;
    gobject_class->get_property=track_box_widget_get_property;
    gobject_class->finalize=track_box_widget_finalize;
   
    GParamSpec *obj_properties = 
            g_param_spec_variant("audio_in_devices","Audio In Devices","Audio In Devices",G_VARIANT_TYPE_ARRAY,NULL,G_PARAM_READWRITE);
    

    
    g_object_class_install_property(gobject_class, PROP_AUDIO_DEVICES_IN,obj_properties);
    
  gtk_widget_class_set_template_from_resource (widget_class,
                                               "/com/tuxin/tuxmusicstudio/track_box.glade");
 gtk_widget_class_bind_template_child_private(widget_class, TrackBoxWidget, record_toggle_button);
 gtk_widget_class_bind_template_child_private(widget_class, TrackBoxWidget, track_label);
 gtk_widget_class_bind_template_child_private(widget_class, TrackBoxWidget, track_device_combobox);
 gtk_widget_class_bind_template_child_private(widget_class, TrackBoxWidget, track_volume_levelbar);
 
 gtk_widget_class_bind_template_callback(widget_class, draw_callback);
 gtk_widget_class_bind_template_callback(widget_class,track_device_combobox_changed_cb);
 gtk_widget_class_bind_template_callback(widget_class,record_toggle_button_toggled_cb);
 
}


TrackBoxWidget *track_box_widget_new(GtkWidget *track_data_widget,GVariant *audio_in_devices) {
    // can add more parameters here
    TrackBoxWidget *widget = g_object_new(TRACK_BOX_WIDGET_TYPE,"audio_in_devices", audio_in_devices,NULL);
    widget->track_data_widget=TRACK_DATA_WIDGET(track_data_widget);
    widget->audio_in_pipeline=tux_gst_create_app_pipeline_without_source(bus_call,new_sample, widget);
    return widget;
}

